package cn.cqautotest.cqviehelper.presenter;

import android.content.Context;
import android.content.SharedPreferences;
import android.util.ArrayMap;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleObserver;

import com.blankj.utilcode.util.EncryptUtils;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.LogUtils;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.zhuangfei.timetable.model.Schedule;

import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import cn.cqautotest.cqviehelper.base.BaseApplication;
import cn.cqautotest.cqviehelper.beans.CourseInfo;
import cn.cqautotest.cqviehelper.config.Constants;
import cn.cqautotest.cqviehelper.interfaces.ICourse;
import cn.cqautotest.cqviehelper.interfaces.ICourseCallback;
import cn.cqautotest.cqviehelper.utils.ColorPool;
import cn.cqautotest.cqviehelper.utils.HandlerUtils;
import cn.cqautotest.cqviehelper.utils.network.MyOkHttp;
import okhttp3.FormBody;
import okhttp3.Headers;

/**
 * 一个通过简单调用，从而获取课程信息的 Presenter 类
 */
public class CoursePresenter implements ICourse, LifecycleObserver {

    private static final String TAG = CoursePresenter.class.getSimpleName();
    public static boolean IS_DEBUG = false;
    private final List<ICourseCallback> mCallbacks = new ArrayList<>();
    private final CopyOnWriteArrayList<Schedule> realSchedules = new CopyOnWriteArrayList<>();
    private String mUserId = null;
    private String mSchoolYear = null;
    private int mTerm = 1;
    private int mWeek = 1;

    private static volatile CoursePresenter singleton = null;
    private final SharedPreferences mSp;
    private static SharedPreferences mSpUserInfoSetting;

    private CoursePresenter() {
        mSp = BaseApplication.getContext().getSharedPreferences(Constants.COURSE_LIST, Context.MODE_PRIVATE);
        loadLocalCourse();
    }

    public static CoursePresenter getInstance() {
        if (singleton == null) {
            synchronized (CoursePresenter.class) {
                if (singleton == null) {
                    singleton = new CoursePresenter();
                    mSpUserInfoSetting = BaseApplication.getContext().getSharedPreferences(Constants.USER_INFO_SETTING, Context.MODE_PRIVATE);
                }
            }
        }
        return singleton;
    }

    @Override
    public void registered(ICourseCallback callback) {
        if (!mCallbacks.contains(callback)) {
            mCallbacks.add(callback);
        }
    }

    @Override
    public void unRegistered(ICourseCallback callback) {
        mCallbacks.remove(callback);
    }

    @Override
    public int getWeek() {
        return mWeek;
    }

    @Override
    public void clearLocalCourseList() {
        realSchedules.clear();
        saveCourseMap2Local();
    }

    /**
     * 当用户配置发生改变的时候，清空本地数据和内存数据，并通知UI更新.
     */
    @Override
    public void onUserInfoChanged() {
        mCallbacks.forEach(ICourseCallback::reSetUpUserInfo);
    }

    @Override
    public void reGetCourse() {
        getCourseByWeek(mUserId, mSchoolYear, mTerm, mWeek);
    }

    /**
     * 可以直接通过调用该方法获得课程信息的Course实体类
     *
     * @param userId     学工号 1831613219
     * @param schoolYear 年份 2019-2020
     * @param term       学期 1
     * @param week       第几周 4
     * @return JsonString ---> courseInfo
     * @throws Exception
     */
    @Override
    public void getCourseByWeek(String userId, String schoolYear, int term, int week) {
        mUserId = userId;
        mSchoolYear = schoolYear;
        mTerm = term;
        mWeek = week;
        HandlerUtils.getBackHandler().post(() -> {
            try {
                CourseInfo courseInfo = getCourseJson(mUserId, mSchoolYear, mTerm, mWeek, CourseInfo.class);
                HandlerUtils.getMainHandler().post(() -> {
                    parseSchedule(courseInfo);
                    handleSuccess(deduplication());
                });
            } catch (Exception e) {
                e.printStackTrace();
                handleError(e);
            }
        });
    }

    /**
     * 加载本地课程数据
     */
    private void loadLocalCourse() {
        String localCourseJson = mSp.getString(Constants.LOCAL_COURSE_LIST, GsonUtils.toJson(new ArrayList<Schedule>()));
        realSchedules.clear();
        try {
            realSchedules.addAll(GsonUtils.fromJson(localCourseJson, new TypeToken<ArrayList<Schedule>>() {
            }.getType()));
        } catch (JsonSyntaxException | NullPointerException e) {
            e.printStackTrace();
            //兼容上一个版本的数据存储方式，否则APP会崩溃。
            saveCourseMap2Local();
        }
    }


    /**
     * 正常获取数据时的回调通知
     *
     * @param schedules 当前查询周次的课程信息
     */
    private void handleSuccess(List<Schedule> schedules) {
        mCallbacks.forEach(callback -> callback.onCourseLoaded(schedules));
    }

    /**
     * 处理异常时的回调通知
     *
     * @param e
     */
    private void handleError(Exception e) {
        HandlerUtils.getMainHandler().post(() -> {
            loadLocalCourse();
            mCallbacks.forEach(callback -> callback.onRequestError(e));
        });
    }

    /**
     * 解析课程数据
     *
     * @param courseInfo 服务器返回的课程信息的数据格式
     * @return realSchedules 最新的课程数据集
     */
    @NonNull
    private void parseSchedule(CourseInfo courseInfo) {
        int week = 1;
        if (courseInfo.getCode().equals(Constants.COURSE_SUCCESS_CODE)) {
            ArrayList<Schedule> schedules = new ArrayList<>();
            CourseInfo.ContentBean content = courseInfo.getContent();
            List<CourseInfo.ContentBean.ListBean> courseList = content.getList();
            for (CourseInfo.ContentBean.ListBean listBean : courseList) {
                ArrayList<Integer> weekList = new ArrayList<>();
                String[] split = listBean.getWeek().split(",");
                for (String s : split) {
                    weekList.add(str2number(s));
                }
                Schedule schedule = new Schedule();
                schedule.setName(listBean.getCourseName());
                schedule.setRoom(listBean.getClassroomName());
                schedule.setTeacher(listBean.getTeacherName());
                schedule.setWeekList(weekList);
                schedule.setStart(str2number(listBean.getStartTime()));
                schedule.setStep(str2number(listBean.getEndTime()) - str2number(listBean.getStartTime()) + 1);
                schedule.setDay(str2number(listBean.getWeekDay()));
                schedule.setColorRandom(ColorPool.getRandomColor());
                schedules.add(schedule);
            }
            realSchedules.clear();
            realSchedules.addAll(schedules);
            saveCourseMap2Local();
        }
    }

    /**
     * 保存课程数据到本地
     */
    public CoursePresenter saveCourseMap2Local() {
        mSp.edit().putString(Constants.LOCAL_COURSE_LIST, GsonUtils.toJson(deduplication())).apply();
        return this;
    }

    /**
     * 类型转换
     *
     * @param string
     * @return
     */
    private int str2number(String string) {
        try {
            return Integer.parseInt(string.trim());
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return -1;
        }
    }

    private <T> T getCourseJson(String userId, String schoolYear, int term, int week, Class<T> tClazz) throws Exception {
        String courseJson = getCourseJson(userId, schoolYear, term, week);
        return GsonUtils.fromJson(courseJson, tClazz);
    }

    /**
     * 可以直接通过调用该方法获得课程信息的Json字符串
     *
     * @param userId     学工号 1831613219
     * @param schoolYear 年份 2019-2020
     * @param term       学期 1
     * @param week       第几周 4
     * @return JsonString ---> courseInfo
     * @throws Exception
     */
    private String getCourseJson(String userId, String schoolYear, int term, int week) throws Exception {
        long currentTime = System.currentTimeMillis();
        long nonce = (currentTime + currentTime << 1) / 3;
        String userType = mSpUserInfoSetting.getString(Constants.USER_TYPE, "STUDENT");
//        String path = "/normalservice/course/selectCourse?appKey=" + Constants.APP_KEY + "&clientCategory=PC&isWeek=YES" +
//                "&nonce=" + nonce + "&operatorId=" + userId + "&schoolYear=" + schoolYear + "&term=" + term +
//                "&timestamp=" + currentTime + "&universityId=102574&userType=" + userType + "&week=" + week;
//        String MD5Signature = MD5Utils.stringToMD5(path + "&appSecret=" + Constants.APP_SECRET);
//        if (IS_DEBUG) {
//            LogUtils.d(this, "getData: =========> path string is " + path);
//            LogUtils.d(this, "getData: =========> md5 string is " + MD5Signature);
//            LogUtils.d(this, "getData: =========> url string is " + Constants.COURSE_BASE_URL.concat(path).concat("&sign=").concat(MD5Signature));
//        }
        StringBuilder sb = new StringBuilder();
        sb.append("/normalservice/course/selectCourse?");
        FormBody.Builder builder = MyOkHttp.getFormBody();
        FormBody formBody = builder
                .add("appKey", Constants.APP_KEY)
                .add("clientCategory", "PC")
                .add("isWeek", "NO")
                .add("nonce", String.valueOf(nonce))
                .add("operatorId", userId)
                .add("schoolYear", schoolYear)
                .add("term", String.valueOf(term))
                .add("timestamp", String.valueOf(currentTime))
                .add("universityId", String.valueOf(102574))
                .add("userType", userType)
                .add("week", String.valueOf(week))
                .build();
        int size = formBody.size();
        for (int i = 0; i < size; i++) {
            sb.append(formBody.name(i));
            sb.append("=");
            sb.append(formBody.value(i));
            sb.append("&");
        }
        int lastIndexOf = sb.lastIndexOf("&");
        sb.deleteCharAt(lastIndexOf);
        String sign = EncryptUtils.encryptMD5ToString(sb.toString().concat("&appSecret=").concat(Constants.APP_SECRET));
        LogUtils.d(this, String.format("request path is =====>%s", Constants.COURSE_BASE_URL.concat(sb.toString()).concat("&sign=".concat(sign))));
        String realPath = sb.toString();
        sb.delete(0, sb.length())
                .insert(0, Constants.COURSE_BASE_URL)
                .append(realPath)
                .append("&sign=")
                .append(sign);
        realPath = sb.toString();
        LogUtils.d(this, realPath);
        return MyOkHttp.postData(realPath, new FormBody.Builder(), getHeaders());
    }

    private Headers getHeaders() {
        ArrayMap<String, String> headers = new ArrayMap<>();
        headers.put("Accept", "application/json, text/plain, */*");
        headers.put("Accept-Language", "zh-CN,zh;q=0.9");
        //headers.put("Cookie", "AUTHENTICATION_NORMAL_LOGIN=4F187D7F4569CD490D01A45BB1EA66FB; userInfo={%22operatorId%22:%22YK0000010000200003%22%2C%22userType%22:%22STUDENT%22%2C%22userId%22:null}; ump_token_pc-officeHall=1596337975196zd2ouI4");
        headers.put("Connection", "keep-alive");
        headers.put("Host", "ai.cqvie.edu.cn");
        headers.put("Referer", "http://ai.cqvie.edu.cn/lightapp/pcapp.html");
        headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
        return Headers.of(headers);
    }

    public List<Schedule> deduplication() {
        return realSchedules;
    }
}
